// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.linqingying.gitcode.util

import com.intellij.collaboration.ui.SimpleEventListener
import com.intellij.concurrency.JobScheduler
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.progress.ProgressIndicator
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.NlsSafe
import com.intellij.openapi.util.Pair
import com.intellij.openapi.util.ThrowableComputable
import com.intellij.openapi.util.text.StringUtil
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.util.EventDispatcher
import git4idea.remote.hosting.GitHostingUrlUtil.match
import git4idea.repo.GitRemote
import git4idea.repo.GitRepository
import org.jetbrains.annotations.ApiStatus
import com.linqingying.gitcode.authentication.GitCodeAuthenticationManager
import java.io.IOException
import java.net.UnknownHostException
import java.util.concurrent.ScheduledFuture
import java.util.concurrent.TimeUnit
import kotlin.properties.ObservableProperty
import kotlin.reflect.KProperty

/**
 * Various utility methods for the GutHub plugin.
 */
object GitCodeUtil {

  @JvmField
  val LOG: Logger = Logger.getInstance("gitcode")

  @NlsSafe
  const val SERVICE_DISPLAY_NAME: String = "GitCode"

  @NlsSafe
  const val ENTERPRISE_SERVICE_DISPLAY_NAME: String = "GitCode Enterprise"
  const val GIT_AUTH_PASSWORD_SUBSTITUTE: String = "x-oauth-basic"

  @JvmStatic
  fun addCancellationListener(run: () -> Unit): ScheduledFuture<*> {
    return JobScheduler.getScheduler().scheduleWithFixedDelay(run, 1000, 300, TimeUnit.MILLISECONDS)
  }

  private fun addCancellationListener(indicator: ProgressIndicator, thread: Thread): ScheduledFuture<*> {
    return addCancellationListener { if (indicator.isCanceled) thread.interrupt() }
  }

  @Throws(IOException::class)
  @JvmStatic
  fun <T> runInterruptable(indicator: ProgressIndicator,
                           task: ThrowableComputable<T, IOException>): T {
    var future: ScheduledFuture<*>? = null
    try {
      val thread = Thread.currentThread()
      future = addCancellationListener(indicator, thread)

      return task.compute()
    }
    finally {
      future?.cancel(true)
      Thread.interrupted()
    }
  }

  @NlsSafe
  @JvmStatic
  fun getErrorTextFromException(e: Throwable): String {
    return if (e is UnknownHostException) {
      "Unknown host: " + e.message
    }
    else StringUtil.notNullize(e.message, "Unknown error")
  }

  object Delegates {

    fun <T> observableField(initialValue: T, dispatcher: EventDispatcher<SimpleEventListener>): ObservableProperty<T> {
      return object : ObservableProperty<T>(initialValue) {
        override fun afterChange(property: KProperty<*>, oldValue: T, newValue: T) = dispatcher.multicaster.eventOccurred()
      }
    }
  }

  @JvmStatic
  @Deprecated("{@link GitCodeGitHelper}", ReplaceWith("GitCodeGitHelper.findGitRepository(project, file)",
                                                     "com.linqingying.gitcode.util.GitCodeGitHelper"))
  @ApiStatus.ScheduledForRemoval
  fun getGitRepository(project: Project, file: VirtualFile?): GitRepository? {
    return GitCodeGitHelper.findGitRepository(project, file)
  }

  @JvmStatic
  @Deprecated("{@link GitCodeGitHelper}")
  private fun findGitCodeRemoteUrl(repository: GitRepository): String? {
    val remote = findGitCodeRemote(repository) ?: return null
    return remote.getSecond()
  }

  @JvmStatic
  @Deprecated("{@link com.linqingying.gitcode.api.GitCodeServerPath}, {@link GitCodeGitHelper}")
  private fun findGitCodeRemote(repository: GitRepository): Pair<GitRemote, String>? {
    val server = GitCodeAuthenticationManager.getInstance().getSingleOrDefaultAccount(repository.project)?.server ?: return null

    var gitcodeRemote: Pair<GitRemote, String>? = null
    for (gitRemote in repository.remotes) {
      for (remoteUrl in gitRemote.urls) {
        if (match(server.toURI(), remoteUrl)) {
          val remoteName = gitRemote.name
          if ("gitcode" == remoteName || "origin" == remoteName) {
            return Pair.create(gitRemote, remoteUrl)
          }
          if (gitcodeRemote == null) {
            gitcodeRemote = Pair.create(gitRemote, remoteUrl)
          }
          break
        }
      }
    }
    return gitcodeRemote
  }

  @JvmStatic
  @Deprecated("{@link com.linqingying.gitcode.api.GitCodeServerPath}")
  @ApiStatus.ScheduledForRemoval
  fun isRepositoryOnGitCode(repository: GitRepository): Boolean {
    return findGitCodeRemoteUrl(repository) != null
  }
}
